{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modFlotteVaisseauxChoix;

interface

uses Classes, SysUtils, modBaseType, modUnitAndDefInfos;




type

(*
   <tr height="20">
    <th><a title="Vitesse: 15000">Grand transporteur</a></th>
    <th>29<input type="hidden" name="maxship203" value="29"/></th>
<!--    <th>15000 -->
     <input type="hidden" name="consumption203" value="50"/>
     <input type="hidden" name="speed203" value="15000" /></th>
     <input type="hidden" name="capacity203" value="25000" /></th>
     <th><a href="javascript:maxShip('ship203');" >max</a> </th>
     <th><input name="ship203" size="10" value="0" alt="Grand transporteur 29"/></th>
   </tr>
   *)

    TVaisseauSelInfos = record
         // GID du vaisseau  (unique)
         GID: integer;

         // Nom du vaisseau (optionnel car on connait une table d'association des GID normalement)
         GIDNom : string;

         // Maximum de ce type de vaisseaux
         Max: integer;

         // Vitesse de ce type de vaisseau
         Speed: integer;

         Consomation: integer;

         // Capacité de fret (de transport)
         Capacite: integer;

         // Regroupe les inputs ci-dessus
         AllHiddenInput: string;

         // Input de sélection du nombre de vaisseau que l'on veut
         // ATTENTION: pour les satellite solaire ce champs n'existe pas
         // mais tout les autre doivent quand même figurer dans le formulaire
         InputNameForChoix: string;


         // Rempli par le programme (0 par défaut)
         QuantiteVoulu: integer;
    end;



    PVaisseauSelInfos = ^TVaisseauSelInfos;

    TCollectionVaisseauSelInfos = class(TList)
    private
       {--------------------------------------------------}
       {Elements spécialisés de la classes}
       // Pour déterminer combien de points de défense la flotte apporte
       // celle-ci devrait être diviser par un nombre de proportion
       FScoreFlotte: integer;

       // Pour déterminer combien de recycleur il faut pour recycler la flotte
       FRessourceTotal: TOGRessourceAll;

       // Quantite de ressource recyclable (30% du métal/cristal)
       FRessourceRecyclable: integer;

       // Somme des ressources totales
       FRessourceTotalSum: integer;


       {--------------------------------------------------}
       {Gestion des pointeurs}
       // Utilisé pour la propriété Itemx
       function GetItemx(Index: integer): TVaisseauSelInfos;

       procedure Init;
    public
       property ScoreFlotte        : integer         read FScoreFlotte;
       property RessourceTotal     : TOGRessourceAll read FRessourceTotal;
       property RessourceRecyclable: integer         read FRessourceRecyclable;
       property RessourceTotalSum  : integer         read FRessourceTotalSum;
       
       // Duplique également les données pointées
       function ImportAndCopyList(List: TList; bResetQtVoulu: boolean): integer;

       function GetDataToPost: string;

       // après avoir sélectionner les vaisseaux, on appel cette fonction pour
       // savoir la vitesse de déplacement utilisée
       function GetSpeedUsed: integer;


       // Vérifie si il existe des vaisseax autres que des sat solaire
       function ExistVaisseauxDeplacable: boolean;

       // Renvoi true si réussit, sinon False (renvoi False également si Quantite est plus grand que le nb de vaisseaux disponible)
       function SetVaisseauxAUtiliser(GID: integer; Quantite: integer): boolean;

       function GetNbVaisseauxDispo(GID: integer): integer;


       // Renvoi le nombre de vaisseaux actuellement sélectionnés
       function GetNbVaisseauxSelectionnes: integer;


       // Vérifie si un champ quantite voulu contient au moins 1 vaisseaux
       function DoExistVaisseauxAEnvoyer: boolean;


       function GetConsomationDeut(Distance, Duree: integer; VitesseRatio: integer = 100): integer;


       // renvoit le nombre de vaisseaux qui seront envoyés
       function SelectionneTousLesVaisseaux: integer;
       procedure DeSelectionneTousLesVaisseaux;


       // Détermine la capacité totale pour les vaisseaux sélectionnées
       function GetCapacitePourVaisseauxSel: integer;


       {--------------------------------------------------}
       {Gestion des pointeurs}
       // *** Ajout de pointeurs
       // Renvoi le pointeur de l'élément ajouté
       function Add(Element: TVaisseauSelInfos): PVaisseauSelInfos;
      
       // Simple ajout de pointeur
       procedure SimpleAdd(Pointeur: PVaisseauSelInfos);
      
       // Renvoi le pointeur de l'élément ajouté/modifié
       // Utilise Get_Index pour vérifier si l'élément doit être mis à jour
       function Add_Update(Element: TVaisseauSelInfos): PVaisseauSelInfos;
      

       // *** Suppression de pointeur
       // Libère et supprime le pointeur Index
       // Renvoi True si réussit, False si hors limite ou erreur
       function Delete(Index: integer): boolean;

      
       // *** Effacement de la liste de pointeur
       procedure Clear; override;
      
      
       // *** Requete sur la liste de pointeur
       function Get_Index(const Element: integer): integer;
       function Get_Pointeur(const Element: integer): PVaisseauSelInfos;
       function Get_Item(Index: integer): TVaisseauSelInfos;
       property Itemx[Index: integer]: TVaisseauSelInfos read GetItemx;


       procedure CalculStats(UnitsInfo: TCollectionUnitInfos);



       {--------------------------------------------------}
       {Procédures et fonctions spécialisées de la classes}
       // Destructeur
       destructor Destroy; override;
       constructor Create;

    end;


implementation


{*******************************************************************************
 ******************************************************************************* 
 **               TCollectionVaisseauSelInfos                                     **
 *******************************************************************************
 *******************************************************************************}

procedure TCollectionVaisseauSelInfos.Init;
begin
    FScoreFlotte := -1;

    FRessourceTotal.Metal     := 0;
    FRessourceTotal.Cristal   := 0;
    FRessourceTotal.Deuterium := 0;
    FRessourceTotal.Energie   := 0;

    FRessourceRecyclable := -1;
    FRessourceTotalSum   := -1;
end;




procedure TCollectionVaisseauSelInfos.CalculStats(UnitsInfo: TCollectionUnitInfos);
var pe   : PVaisseauSelInfos;
    peUnit: PUnitInfos;
    i: integer;
    CurrScore: integer;
begin

    FScoreFlotte := 0;

    FRessourceTotal.Metal     := 0;
    FRessourceTotal.Cristal   := 0;
    FRessourceTotal.Deuterium := 0;
    FRessourceTotal.Energie   := 0;

    FRessourceRecyclable      := 0;
    FRessourceTotalSum        := 0;

    if Count = 0 then exit;

    for i := 0 to Count - 1 do begin
       pe     := Items[i];

       peUnit := UnitsInfo.Get_Pointeur(pe.GID);

       if (peUnit <> nil) and (pe.Max > 0) then begin
          CurrScore    := peUnit.ScoreDef * pe.Max;
          FScoreFlotte := FScoreFlotte + CurrScore;

          FRessourceTotal := mbtAddPrix(FRessourceTotal, peUnit.Prix, pe.Max);
       end;

    end;

    // 30 pourcents du métal et cristal
    FRessourceRecyclable  := (FRessourceTotal.Metal   * 3) div 10;
    FRessourceRecyclable  := FRessourceRecyclable + (FRessourceTotal.Cristal * 3) div 10;

    FRessourceTotalSum := FRessourceTotal.Metal + FRessourceTotal.Cristal + FRessourceTotal.Deuterium;
    
end;


// Duplique également les données pointées
function TCollectionVaisseauSelInfos.ImportAndCopyList(List: TList; bResetQtVoulu: boolean): integer;
var pe   : PVaisseauSelInfos;
    peNew: PVaisseauSelInfos;
    i: integer;
begin
    Result := 0;
    if List.Count = 0 then exit;

    for i := 0 to List.Count - 1 do begin
        pe := List.Items[i];

        peNew := Add(pe^);

        if bResetQtVoulu then
           peNew.QuantiteVoulu := 0;
    end;

end;


function TCollectionVaisseauSelInfos.GetCapacitePourVaisseauxSel: integer;
var i: integer;
    pe : PVaisseauSelInfos;
begin

    Result := 0;
    if Count = 0 then begin
       exit;
    end;


    for i := 0 to Count - 1 do begin
        pe := Items[i];

        if pe.QuantiteVoulu > 0 then begin
           Result := Result + (pe.Capacite * pe.QuantiteVoulu);
        end;

    end;
    
end;

function TCollectionVaisseauSelInfos.GetConsomationDeut(Distance, Duree: integer; VitesseRatio: integer = 100): integer;
var i: integer;
    pe : PVaisseauSelInfos;
    ResultX1: Real;
    CurrVaisseauSpeed, BasicConsomation: Real;
    SpeedUsed: integer;
begin

    SpeedUsed := GetSpeedUsed;

    if SpeedUsed <= 0 then begin
       Result := 0;
       exit;
    end;

    // Convertie le ratio pour l'adapter à la formule
    case VitesseRatio of
          0..14: VitesseRatio := 1;
         15..24: VitesseRatio := 2;
         25..34: VitesseRatio := 3;
         35..44: VitesseRatio := 4;
         45..54: VitesseRatio := 5;
         55..64: VitesseRatio := 6;
         65..74: VitesseRatio := 7;
         75..84: VitesseRatio := 8;
         85..94: VitesseRatio := 9;
         else    VitesseRatio := 10;
    end;



    ResultX1 := 0.00;

(*

  msp = maxspeed();        // SpeedUsed
  sp = speed();            // pe.Speed
  dist = distance();       // Distance
  dur = duration();        // Duree
  speedfactor = document.getElementsByName("speedfactor")[0].value;

  for (i = 200; i < 220; i++) {
    if (document.getElementsByName("ship" + i)[0]) {
      shipspeed = document.getElementsByName("speed" + i)[0].value;
      spd = 35000 / (dur * speedfactor - 10) * Math.sqrt(dist * 10 / shipspeed);

      //spd = Math.max(msp / document.getElementsByName("speed" + i)[0].value, 0.1);
      //spd = Math.min(spd, 1.0);
      //spd = spd * sp;
      //spd = 10;
      basicConsumption = document.getElementsByName("consumption" + i)[0].value
	* document.getElementsByName("ship" + i)[0].value;
      consumption += basicConsumption * dist / 35000 * ((spd / 10) + 1) * ((spd / 10) + 1);
      //      values = values + " " + spd;
    }
  }
*)

    for i := 0 to Count - 1 do begin
        pe := Items[i];

        if pe.QuantiteVoulu > 0 then begin
           // spd = 35000 / (dur - 10) * Math.sqrt(dist * 10 / shipspeed);
           // spd = 35000 / (Duree - 10) * Math.sqrt(Distance * 10 / pe.Speed);
           BasicConsomation := 35000 / (Duree - 10);
           CurrVaisseauSpeed :=  BasicConsomation * sqrt((Distance * 10) / pe.Speed);


           // basicConsumption = document.getElementsByName("consumption" + i)[0].value
	        //                     * document.getElementsByName("ship" + i)[0].value;



           // basicConsumption = pe.Consomation * pe.QuantiteVoulu;
	        BasicConsomation := pe.Consomation * pe.QuantiteVoulu;


           // consumption += basicConsumption * dist / 35000 * ((spd / 10) + 1) * ((spd / 10) + 1);
           // ResultX1 += BasicConsomation * Distance / 35000 * ((CurrVaisseauSpeed / 10) + 1) * ((CurrVaisseauSpeed / 10) + 1);

           CurrVaisseauSpeed := (CurrVaisseauSpeed / 10.0) + 1;
           CurrVaisseauSpeed := sqr(CurrVaisseauSpeed);

           // ResultX1 += BasicConsomation * Distance / 35000 * CurrVaisseauSpeed;
           BasicConsomation := (BasicConsomation * Distance) / 35000.00;
           BasicConsomation := BasicConsomation * CurrVaisseauSpeed;

           ResultX1 := ResultX1 + BasicConsomation;
        end;



    end;

    Result := round(ResultX1) + 1;
    

end;

procedure TCollectionVaisseauSelInfos.DeSelectionneTousLesVaisseaux;
var pe   : PVaisseauSelInfos;
    i: integer;
begin

    if (Count = 0) then exit;

    for i := 0 to Count - 1 do begin
        pe := Items[i];

        if pe.InputNameForChoix <> '' then begin
           pe.QuantiteVoulu := 0;
        end;
    end;

end;


function TCollectionVaisseauSelInfos.SelectionneTousLesVaisseaux: integer;
var pe   : PVaisseauSelInfos;
    i: integer;
begin
    Result := 0;
    if (Count = 0) then exit;

    for i := 0 to Count - 1 do begin
        pe := Items[i];

        if pe.InputNameForChoix <> '' then begin
           pe.QuantiteVoulu := pe.Max;
           Inc(Result, pe.Max);
        end;

    end;

end;



// Renvoi true si réussit, sinon False (renvoi False également si Quantite est plus grand que le nb de vaisseaux disponible)
function TCollectionVaisseauSelInfos.SetVaisseauxAUtiliser(GID: integer; Quantite: integer): boolean;
var i: integer;
    pe : PVaisseauSelInfos;
begin

    Result := false;

    if (Count = 0) or (Quantite < 0) then exit;

    for i := 0 to Count - 1 do begin
        pe := Items[i];

        if pe.GID = GID then begin

           if Quantite > pe.Max then begin
              // On renvoi quand même false sans rien modifier
              exit;
           end;

           pe.QuantiteVoulu := Quantite;
           Result := true;
           exit;
        end;

    end;

end;

function TCollectionVaisseauSelInfos.GetNbVaisseauxSelectionnes: integer;
var i: integer;
    pe : PVaisseauSelInfos;
begin

    Result := 0;

    if Count = 0 then exit;

    for i := 0 to Count - 1 do begin
        pe := Items[i];

        if pe.QuantiteVoulu > 0 then begin
           Result := Result + pe.QuantiteVoulu;
        end;
    end;

    
end;

function TCollectionVaisseauSelInfos.GetNbVaisseauxDispo(GID: integer): integer;
var i: integer;
    pe : PVaisseauSelInfos;
begin

    Result := 0;

    if Count = 0 then exit;

    for i := 0 to Count - 1 do begin
        pe := Items[i];

        if pe.GID = GID then begin
           Result := pe.Max;
           exit;
        end;

    end;

end;

function TCollectionVaisseauSelInfos.DoExistVaisseauxAEnvoyer: boolean;
var i: integer;
    pe : PVaisseauSelInfos;
begin

    if Count = 0 then begin
       // aucun vaisseau
       Result := false;
       exit;
    end;

    for i := 0 to Count - 1 do begin
        pe := Items[i];

        if pe.InputNameForChoix <> '' then begin
           if pe.QuantiteVoulu > 0 then begin
              Result := true;
              exit;
           end;

        end;

    end;

end;

function TCollectionVaisseauSelInfos.ExistVaisseauxDeplacable: boolean;
var i: integer;
    pe : PVaisseauSelInfos;
begin

    if Count = 0 then begin
       // aucun vaisseau
       Result := false;
       exit;
    end;

    i := 1;
    pe := Items[0];

    if (pe.InputNameForChoix = '') and (Count = 1) then begin
       // que des sat solaire
       Result := false;
    end else begin
       Result := true;
    end;



end;

function TCollectionVaisseauSelInfos.GetSpeedUsed: integer;
var i: integer;
    pe : PVaisseauSelInfos;
begin

   if Count = 0 then begin
      Result := -1;
      exit;
   end;

   Result := maxint;

   for i := 0 to Count - 1 do begin
      pe := Items[i];

      if (pe.QuantiteVoulu > 0) and (pe.Speed < Result) then
         Result := pe.Speed;

   end;

   if Result = maxint then
      // aucun vaisseau sélectionné
      Result := 0;
   
end;

function TCollectionVaisseauSelInfos.GetDataToPost: string;
var i: integer;
    pe : PVaisseauSelInfos;
begin

    Result := '';

    if Count = 0 then exit;

    for i := 0 to Count - 1 do begin
       pe := Items[i];

       if i = 0 then
          Result := pe.AllHiddenInput
       else
          Result := Result + '&' + pe.AllHiddenInput;

       if (pe.InputNameForChoix <> '') and (pe.QuantiteVoulu >= 0) then
          // le égal existe déjà
          Result := Result + '&' + pe.InputNameForChoix + IntToStr(pe.QuantiteVoulu);


    end;



end;


constructor TCollectionVaisseauSelInfos.Create;
begin
    
    // Héritage
    InHerited Create;
    
    
    
    // Code pour l'initialisation ici
    Init;

end;

destructor TCollectionVaisseauSelInfos.Destroy;
begin
    // Implémenter le code avant l'héritage de la méthode
    // C'est à dire ici
    
   
    // On appelle NOTRE clear qui lui-même appelle NOTRE Delete qui libère l'espace mémoire
    // avant d'enlever le pointeur de la TList
    // L'héritage de Destroy appelle Clear, mais es-ce le notre ou celui de la classe de base ?
    Clear;
    
    
    // Héritage
    InHerited Destroy;
end;


procedure TCollectionVaisseauSelInfos.Clear;
var i: integer;
begin

    if Count > 0 then begin
        For i := Count -1 downto 0 do
            Delete(i);
    end;
    
    inherited Clear;
end;


function TCollectionVaisseauSelInfos.Get_Pointeur(const Element: integer): PVaisseauSelInfos;
var i: integer;
begin

   Result := nil;
   
   i := Get_Index(Element);
   if i < 0 then exit;
   Result := Items[i];

end;


function TCollectionVaisseauSelInfos.Get_Index(const Element: integer): integer;
var i: integer;
    pElement: PVaisseauSelInfos;
begin

   Result := -1;

   if Count <= 0 then exit;

   for i := 0 to Count - 1 do begin
      pElement := Items[i];

      {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
      {### Neccessite adpation en fonction du type record  ### }
      {### ### ### ### ### ### ### ### ### ### ### ### ### ### }

      if pElement^.GID = Element then begin
         Result := i;
         exit;
      end;
   end;

end;



function TCollectionVaisseauSelInfos.Add_Update(Element: TVaisseauSelInfos): PVaisseauSelInfos;
var i : integer;
begin
     
     Result := nil;
     Init;

                                         {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
     i := Get_Index(Element.GID);        {### Neccessite adpation en fonction du type record  ### }
                                         {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
     if i < 0 then begin 
        // L'élément n'existe pas, l'ajoute
        Result  := Add(Element);
     end else begin
        // L'élément existe déjà, le met à jour
        Result  := Items[i];     // Obtient le pointeur
        Result^ := Element;
     end;
     
end;




function TCollectionVaisseauSelInfos.Get_Item(Index: integer): TVaisseauSelInfos;
var pElement: PVaisseauSelInfos;
begin

     if Index < 0 then exit;
     if Index >= Count then exit;

     pElement := Items[Index];     // Obtient le pointeur
     Result := pElement^;

end;

function TCollectionVaisseauSelInfos.Delete(Index: integer): boolean;
var pElement: PVaisseauSelInfos;
begin

    Result := false;

    if (Index < 0) or (Index >= Count) then
       // Hors limite
       exit;

    Init;

    // Obtient le pointeur
    pElement := Items[Index];

    if pElement <> nil then
       // Libére la mémoire créée lors de l'ajout
       Dispose(pElement);

    // Supprime le pointeur de la liste
    inherited Delete(Index);

    // Met le resultat à True
    Result := true;

end;



function TCollectionVaisseauSelInfos.Add(Element: TVaisseauSelInfos): PVaisseauSelInfos;
var pElement: PVaisseauSelInfos;
begin
    
    Result := nil;
    Init;

    New(pElement);          // Créer l'espace mémoire requis
    pElement^ := Element;   // Recopie les données dans l'emplacement pointé
    
    try
       inherited Add(pElement);  // Ajoute le pointeur à la liste
       Result := pElement;
    except
       Result := nil;
    end;
    
    // Pour libérer la mémoire occupé par l'élément pointer:  Dispose(pElement);

end;

// Simple ajout de pointeur
procedure TCollectionVaisseauSelInfos.SimpleAdd(Pointeur: PVaisseauSelInfos);
begin
    
    inherited Add(Pointeur);  // Ajoute le pointeur à la liste
    Init;
end;

function TCollectionVaisseauSelInfos.GetItemx(Index: integer): TVaisseauSelInfos;
var pElement: PVaisseauSelInfos;
begin

   if Count < 0 then exit;
   if Index >= Count then exit;

   pElement := Items[Index];
   Result := pElement^;

end;


end.
